// 2025/4/8: 首个记录
// 2025/6/12: 协议名常量改为public

package com.appox.samples;

import java.util.ArrayList;
import java.util.List;
import com.appox.Sample;
import com.appox.GeneralSample;
import com.appox.GeneralElement;

/**
 * 交通样本类
 */
public class TrafficSample extends Sample {
    /**
     * 交通元素类型
     */
    public enum TrafficElementType {
        INVALID(0),
        SPEED_LIMIT(1), // 限速标志
        TRAFFIC_SIGN(2), // 交通标志
        TRAFFIC_LIGHT(3), // 交通信号灯
        STOP_LINE(4), // 停止线
        SPEED_BUMP(5), // 减速带
        ARROW_MARK(6), // 路面箭头
        PROHIBITION_ZONE(7), // 禁停区
        PARKING_SLOT(8), // 停车位
        ZEBRA_CROSSING(9); // 斑马线
        
        private final int value;
        
        TrafficElementType(int value) {
            this.value = value;
        }
        
        public int getValue() {
            return value;
        }
        
        public static TrafficElementType fromValue(int value) {
            for (TrafficElementType type : TrafficElementType.values()) {
                if (type.getValue() == value) {
                    return type;
                }
            }
            return INVALID;
        }
    }
    
    /**
     * 交通标志类型
     */
    public enum TrafficSignType {
        STOP(0), // 停止标志

        NO_ENTRY(1), // 禁止驶入
        NO_DRIVING(2), // 禁止通行
        NO_PARKING(3), // 禁止停车
        NO_LONG_TIME_PARKING(4), // 禁止长时间停车
        NO_VEHICLE(5), // 禁止机动车驶入
        NO_TRUCK(6), // 禁止货车驶入
        NO_BUS(7), // 禁止客车驶入
        NO_PEDESTRIAN(8), // 禁止行人进入
        NO_LEFT_TURN(9), // 禁止左转
        NO_RIGHT_TURN(10), // 禁止右转
        NO_ANY_TURN(11), // 禁止转弯
        NO_FORWARD(12), // 禁止直行
        NO_FORWARD_OR_LEFT(13), // 禁止直行或左转
        NO_FORWARD_OR_RIGHT(14), // 禁止直行或右转
        NO_U_TURN(15), // 禁止调头
        NO_HORN(16), // 禁止鸣笛
        NO_OVERTAKE(17), // 禁止超车
        NO_OVERTAKE_ENDER(18), // 禁止超车区间结束
        NO_DANGEROUS_GOODS(19), // 禁止危险品车辆驶入

        LOW_SPEED_LIMIT(50), // 低速限速
        SPEED_LIMIT_ENDER(51), // 限速区间结束
        WEIGHT_LIMIT_WHEEL(52), // 轴重限重
        WEIGHT_LIMIT_TOTAL(53), // 全车限重
        HEIGHT_LIMIT(54), // 高度限制
        WIDTH_LIMIT(55), // 宽度限制
        YIELD(56), // 让行标志
        STOP_AND_CHECK(57), // 停车检查

        GENERAL_SIGN(100), // 指示类标志
        GENERAL_NO_SPECIAL_VEHICLE(101), // 禁止特种车辆驶入
        GENERAL_CONSTRUCTION(102), // 施工标志
        GENERAL_WARNING(103), // 警告牌
        GENERAL_GREEN_BACKGROUND(104), // 绿底指示牌
        GENERAL_BLUE_BACKGROUND(105), // 蓝底指示牌
        GENERAL_RED_BACKGROUND(106), // 红底指示牌
        GENERAL_WHITE_BACKGROUND(107), // 白底指示牌
        GENERAL_YELLOW_ROAD_ID(108), // 黄底道路ID牌
        GENERAL_RED_ROAD_ID(109); // 红底道路ID牌
        
        private final int value;
        
        TrafficSignType(int value) {
            this.value = value;
        }
        
        public int getValue() {
            return value;
        }
        
        public static TrafficSignType fromValue(int value) {
            for (TrafficSignType type : TrafficSignType.values()) {
                if (type.getValue() == value) {
                    return type;
                }
            }
            return STOP;
        }
    }
    
    /**
     * 交通信号灯状态
     */
    public enum TrafficLightState {
        INVALID(0),
        STOP(1), // 红灯
        CONTINUE(2), // 黄灯
        CAUTION(3), // 黄灯闪烁
        ALLOW(4), // 绿灯
        ALLOW_FLASH(5), // 绿灯闪烁
        STOP_FLASH(6); // 红灯闪烁
        
        private final int value;
        
        TrafficLightState(int value) {
            this.value = value;
        }
        
        public int getValue() {
            return value;
        }
        
        public static TrafficLightState fromValue(int value) {
            for (TrafficLightState state : TrafficLightState.values()) {
                if (state.getValue() == value) {
                    return state;
                }
            }
            return INVALID;
        }
    }
    
    /**
     * 停车位占用状态
     */
    public enum ParkingSlotOccupancy {
        UNKNOWN(0), // 未知
        NOT_OCCUPIED(1), // 未被占用
        OCCUPIED(2); // 被占用
        
        private final int value;
        
        ParkingSlotOccupancy(int value) {
            this.value = value;
        }
        
        public int getValue() {
            return value;
        }
        
        public static ParkingSlotOccupancy fromValue(int value) {
            for (ParkingSlotOccupancy state : ParkingSlotOccupancy.values()) {
                if (state.getValue() == value) {
                    return state;
                }
            }
            return UNKNOWN;
        }
    }
    
    /**
     * 箭头组合类
     */
    public static class ArrowCombination {
        private boolean goStraight; // 直行
        private boolean turnLeft; // 左转
        private boolean turnRight; // 右转
        private boolean uTurn; // 调头
        
        public ArrowCombination() {
            this.goStraight = false;
            this.turnLeft = false;
            this.turnRight = false;
            this.uTurn = false;
        }
        
        public boolean isGoStraight() {
            return goStraight;
        }
        
        public void setGoStraight(boolean goStraight) {
            this.goStraight = goStraight;
        }
        
        public boolean isTurnLeft() {
            return turnLeft;
        }
        
        public void setTurnLeft(boolean turnLeft) {
            this.turnLeft = turnLeft;
        }
        
        public boolean isTurnRight() {
            return turnRight;
        }
        
        public void setTurnRight(boolean turnRight) {
            this.turnRight = turnRight;
        }
        
        public boolean isUTurn() {
            return uTurn;
        }
        
        public void setUTurn(boolean uTurn) {
            this.uTurn = uTurn;
        }
    }
    
    /**
     * 交通元素基类
     */
    public static class TrafficElement {
        private TrafficElementType type; // 交通元素类别
        private int id; // 交通元素ID，0表示无效
        private Integer rawId; // 交通元素的原始ID
        
        public TrafficElement() {
            this.id = 0;
        }
        
        public TrafficElementType getType() {
            return type;
        }
        
        public void setType(TrafficElementType type) {
            this.type = type;
        }
        
        public int getId() {
            return id;
        }
        
        public void setId(int id) {
            this.id = id;
        }
        
        public Integer getRawId() {
            return rawId;
        }
        
        public void setRawId(Integer rawId) {
            this.rawId = rawId;
        }
    }
    
    /**
     * 限速标志
     */
    public static class SpeedLimit extends TrafficElement {
        private int limitedSpeed; // [KPH] 限速
        private Double posX; // [m] 纵向位置
        private Double posY; // [m] 横向位置
        private Double posZ; // [m] 垂直位置
        
        public SpeedLimit() {
            setType(TrafficElementType.SPEED_LIMIT);
        }
        
        public int getLimitedSpeed() {
            return limitedSpeed;
        }
        
        public void setLimitedSpeed(int limitedSpeed) {
            this.limitedSpeed = limitedSpeed;
        }
        
        public Double getPosX() {
            return posX;
        }
        
        public void setPosX(Double posX) {
            this.posX = posX;
        }
        
        public Double getPosY() {
            return posY;
        }
        
        public void setPosY(Double posY) {
            this.posY = posY;
        }
        
        public Double getPosZ() {
            return posZ;
        }
        
        public void setPosZ(Double posZ) {
            this.posZ = posZ;
        }
    }
    
    /**
     * 交通标志
     */
    public static class TrafficSign extends TrafficElement {
        private TrafficSignType signType; // 交通标志类别
        private Double posX; // [m] 纵向位置
        private Double posY; // [m] 横向位置
        private Double posZ; // [m] 垂直位置
        
        public TrafficSign() {
            setType(TrafficElementType.TRAFFIC_SIGN);
        }
        
        public TrafficSignType getSignType() {
            return signType;
        }
        
        public void setSignType(TrafficSignType signType) {
            this.signType = signType;
        }
        
        public Double getPosX() {
            return posX;
        }
        
        public void setPosX(Double posX) {
            this.posX = posX;
        }
        
        public Double getPosY() {
            return posY;
        }
        
        public void setPosY(Double posY) {
            this.posY = posY;
        }
        
        public Double getPosZ() {
            return posZ;
        }
        
        public void setPosZ(Double posZ) {
            this.posZ = posZ;
        }
    }
    
    /**
     * 交通信号灯
     */
    public static class TrafficLight extends TrafficElement {
        private TrafficLightState state; // 信号灯状态
        private ArrowCombination arrows; // 信号灯的箭头组合，若不分方向(实心圆信号)则为空
        private Double posX; // [m] 纵向位置
        private Double posY; // [m] 横向位置
        private Double posZ; // [m] 垂直位置
        private Double stopPosX; // [m] 纵向停止位置
        private Double stopPosY; // [m] 横向停止位置
        
        public TrafficLight() {
            setType(TrafficElementType.TRAFFIC_LIGHT);
        }
        
        public TrafficLightState getState() {
            return state;
        }
        
        public void setState(TrafficLightState state) {
            this.state = state;
        }
        
        public ArrowCombination getArrows() {
            return arrows;
        }
        
        public void setArrows(ArrowCombination arrows) {
            this.arrows = arrows;
        }
        
        public Double getPosX() {
            return posX;
        }
        
        public void setPosX(Double posX) {
            this.posX = posX;
        }
        
        public Double getPosY() {
            return posY;
        }
        
        public void setPosY(Double posY) {
            this.posY = posY;
        }
        
        public Double getPosZ() {
            return posZ;
        }
        
        public void setPosZ(Double posZ) {
            this.posZ = posZ;
        }
        
        public Double getStopPosX() {
            return stopPosX;
        }
        
        public void setStopPosX(Double stopPosX) {
            this.stopPosX = stopPosX;
        }
        
        public Double getStopPosY() {
            return stopPosY;
        }
        
        public void setStopPosY(Double stopPosY) {
            this.stopPosY = stopPosY;
        }
    }
    
    /**
     * 停止线
     */
    public static class StopLine extends TrafficElement {
        private double distance; // [m] 停止线与x轴相交位置
        private Double heading; // [°] 停止线法线朝向，逆时针为正方向
        
        public StopLine() {
            setType(TrafficElementType.STOP_LINE);
        }
        
        public double getDistance() {
            return distance;
        }
        
        public void setDistance(double distance) {
            this.distance = distance;
        }
        
        public Double getHeading() {
            return heading;
        }
        
        public void setHeading(Double heading) {
            this.heading = heading;
        }
    }
    
    /**
     * 减速带
     */
    public static class SpeedBump extends TrafficElement {
        private double distance; // [m] 减速带与x轴相交位置
        private Double heading; // [°] 减速带法线朝向，逆时针为正方向
        
        public SpeedBump() {
            setType(TrafficElementType.SPEED_BUMP);
        }
        
        public double getDistance() {
            return distance;
        }
        
        public void setDistance(double distance) {
            this.distance = distance;
        }
        
        public Double getHeading() {
            return heading;
        }
        
        public void setHeading(Double heading) {
            this.heading = heading;
        }
    }
    
    /**
     * 路面箭头
     */
    public static class ArrowMark extends TrafficElement {
        private ArrowCombination arrows; // 路面标志的箭头组合
        private Double posX; // [m] 纵向位置
        private Double posY; // [m] 横向位置
        
        public ArrowMark() {
            setType(TrafficElementType.ARROW_MARK);
            arrows = new ArrowCombination();
        }
        
        public ArrowCombination getArrows() {
            return arrows;
        }
        
        public void setArrows(ArrowCombination arrows) {
            this.arrows = arrows;
        }
        
        public Double getPosX() {
            return posX;
        }
        
        public void setPosX(Double posX) {
            this.posX = posX;
        }
        
        public Double getPosY() {
            return posY;
        }
        
        public void setPosY(Double posY) {
            this.posY = posY;
        }
    }
    
    /**
     * 禁停区
     */
    public static class ProhibitionZone extends TrafficElement {
        private double centerX; // [m] 禁停区中央的纵向位置
        private double centerY; // [m] 禁停区中央的横向位置
        private double width; // [m] 禁停区的宽度
        private double length; // [m] 禁停区的长度
        private Double heading; // [°] 禁停区的朝向(沿长度方向)，逆时针为正方向
        
        public ProhibitionZone() {
            setType(TrafficElementType.PROHIBITION_ZONE);
        }
        
        public double getCenterX() {
            return centerX;
        }
        
        public void setCenterX(double centerX) {
            this.centerX = centerX;
        }
        
        public double getCenterY() {
            return centerY;
        }
        
        public void setCenterY(double centerY) {
            this.centerY = centerY;
        }
        
        public double getWidth() {
            return width;
        }
        
        public void setWidth(double width) {
            this.width = width;
        }
        
        public double getLength() {
            return length;
        }
        
        public void setLength(double length) {
            this.length = length;
        }
        
        public Double getHeading() {
            return heading;
        }
        
        public void setHeading(Double heading) {
            this.heading = heading;
        }
    }
    
    /**
     * 停车位
     */
    public static class ParkingSlot extends TrafficElement {
        private double centerX; // [m] 停车位中央的纵向位置
        private double centerY; // [m] 停车位中央的横向位置
        private double width; // [m] 停车位的宽度
        private double length; // [m] 停车位的长度
        private Double heading; // [°] 停车位的朝向(沿长度方向)，逆时针为正方向
        private ParkingSlotOccupancy occupancy; // 停车位是否被占用
        
        public ParkingSlot() {
            setType(TrafficElementType.PARKING_SLOT);
        }
        
        public double getCenterX() {
            return centerX;
        }
        
        public void setCenterX(double centerX) {
            this.centerX = centerX;
        }
        
        public double getCenterY() {
            return centerY;
        }
        
        public void setCenterY(double centerY) {
            this.centerY = centerY;
        }
        
        public double getWidth() {
            return width;
        }
        
        public void setWidth(double width) {
            this.width = width;
        }
        
        public double getLength() {
            return length;
        }
        
        public void setLength(double length) {
            this.length = length;
        }
        
        public Double getHeading() {
            return heading;
        }
        
        public void setHeading(Double heading) {
            this.heading = heading;
        }
        
        public ParkingSlotOccupancy getOccupancy() {
            return occupancy;
        }
        
        public void setOccupancy(ParkingSlotOccupancy occupancy) {
            this.occupancy = occupancy;
        }
    }
    
    /**
     * 斑马线
     */
    public static class ZebraCrossing extends TrafficElement {
        private double nearDistance; // [m] 斑马线近端与x轴相交位置
        private Double farDistance; // [m] 斑马线远端与x轴相交位置
        private Double heading; // [°] 斑马线的朝向，逆时针为正方向
        
        public ZebraCrossing() {
            setType(TrafficElementType.ZEBRA_CROSSING);
        }
        
        public double getNearDistance() {
            return nearDistance;
        }
        
        public void setNearDistance(double nearDistance) {
            this.nearDistance = nearDistance;
        }
        
        public Double getFarDistance() {
            return farDistance;
        }
        
        public void setFarDistance(Double farDistance) {
            this.farDistance = farDistance;
        }
        
        public Double getHeading() {
            return heading;
        }
        
        public void setHeading(Double heading) {
            this.heading = heading;
        }
    }

    private List<TrafficElement> elements; // 交通元素列表
    
    /**
     * 构造函数
     */
    public TrafficSample() {
        elements = new ArrayList<>();
    }
    
    /**
     * 获取交通元素列表
     */
    public List<TrafficElement> getElements() {
        return elements;
    }
    
    /**
     * 设置交通元素列表
     */
    public void setElements(List<TrafficElement> elements) {
        this.elements = elements;
    }
    
    /**
     * 获取样本协议
     */
    @Override
    public String getGeneralSampleProtocol() {
        return TARGET_PROTOCOL;
    }
    
    /**
     * 获取所有支持的协议
     */
    @Override
    public String[] getGeneralSampleProtocols() {
        return PROTOCOLS;
    }
    
    /**
     * 将交通样本转换为通用样本
     */
    @Override
    public GeneralSample toGeneralSample(Integer channel) {
        GeneralSample sample = new GeneralSample();
        sample.setTime(this);
        sample.setProtocol(TARGET_PROTOCOL);
        sample.setChannel(channel);

        int[] offsets = new int[elements.size()];
        int[] lengths = new int[elements.size()];
        int totalDataSize = 0;
        
        // 计算各元素数据块的偏移和长度
        for (int i = 0; i < elements.size(); i++) {
            int len = 0;
            TrafficElement element = elements.get(i);
            
            switch (element.getType()) {
                case SPEED_LIMIT:
                    len = 6;
                    break;
                case TRAFFIC_SIGN:
                    len = 5;
                    break;
                case TRAFFIC_LIGHT:
                    len = 10;
                    break;
                case STOP_LINE:
                    len = 4;
                    break;
                case SPEED_BUMP:
                    len = 4;
                    break;
                case ARROW_MARK:
                    len = 8;
                    break;
                case PROHIBITION_ZONE:
                    len = 7;
                    break;
                case PARKING_SLOT:
                    len = 7;
                    break;
                case ZEBRA_CROSSING:
                    len = 5;
                    break;
                default:
                    break;
            }
            
            offsets[i] = totalDataSize;
            lengths[i] = len;
            totalDataSize += len;
        }

        // 创建数据数组
        GeneralElement[] data = new GeneralElement[1 + elements.size() * 6 + totalDataSize];
        
        // 初始化所有元素为空元素
        for (int i = 0; i < data.length; i++) {
            data[i] = new GeneralElement();
        }
        
        int dataBase = 1 + elements.size() * 6;

        // 设置元素数量
        data[0] = new GeneralElement(elements.size());
        
        // 填充元素头部信息
        for (int i = 0; i < elements.size(); i++) {
            TrafficElement elem = elements.get(i);
            data[1 + 6 * i] = new GeneralElement(offsets[i]);
            data[2 + 6 * i] = new GeneralElement(lengths[i]);
            data[3 + 6 * i] = new GeneralElement(elem.getType().getValue());
            data[4 + 6 * i] = new GeneralElement(elem.getId());
            data[5 + 6 * i] = elem.getRawId() != null ? new GeneralElement(elem.getRawId()) : new GeneralElement();

            // 根据元素类型填充详细数据
            int eBase = dataBase + offsets[i];
            switch (elem.getType()) {
                case SPEED_LIMIT:
                    {
                        SpeedLimit speedLimit = (SpeedLimit) elem;
                        data[eBase] = new GeneralElement(speedLimit.getLimitedSpeed());
                        if (speedLimit.getPosX() != null) data[eBase + 1] = new GeneralElement(speedLimit.getPosX());
                        if (speedLimit.getPosY() != null) data[eBase + 2] = new GeneralElement(speedLimit.getPosY());
                        if (speedLimit.getPosZ() != null) data[eBase + 3] = new GeneralElement(speedLimit.getPosZ());
                    }
                    break;
                case TRAFFIC_SIGN:
                    {
                        TrafficSign trafficSign = (TrafficSign) elem;
                        if (trafficSign.getPosX() != null) data[eBase] = new GeneralElement(trafficSign.getPosX());
                        if (trafficSign.getPosY() != null) data[eBase + 1] = new GeneralElement(trafficSign.getPosY());
                        if (trafficSign.getPosZ() != null) data[eBase + 2] = new GeneralElement(trafficSign.getPosZ());
                        data[eBase + 3] = new GeneralElement(trafficSign.getSignType().getValue());
                    }
                    break;
                case TRAFFIC_LIGHT:
                    {
                        TrafficLight trafficLight = (TrafficLight) elem;
                        data[eBase] = new GeneralElement(trafficLight.getState().getValue());
                        if (trafficLight.getPosX() != null) data[eBase + 1] = new GeneralElement(trafficLight.getPosX());
                        if (trafficLight.getPosY() != null) data[eBase + 2] = new GeneralElement(trafficLight.getPosY());
                        if (trafficLight.getPosZ() != null) data[eBase + 3] = new GeneralElement(trafficLight.getPosZ());
                        if (trafficLight.getArrows() != null) {
                            data[eBase + 4] = new GeneralElement(trafficLight.getArrows().isGoStraight() ? 1 : 0);
                            data[eBase + 5] = new GeneralElement(trafficLight.getArrows().isTurnLeft() ? 1 : 0);
                            data[eBase + 6] = new GeneralElement(trafficLight.getArrows().isTurnRight() ? 1 : 0);
                            data[eBase + 7] = new GeneralElement(trafficLight.getArrows().isUTurn() ? 1 : 0);
                        }
                        if (trafficLight.getStopPosX() != null) data[eBase + 8] = new GeneralElement(trafficLight.getStopPosX());
                        if (trafficLight.getStopPosY() != null) data[eBase + 9] = new GeneralElement(trafficLight.getStopPosY());
                    }
                    break;
                case STOP_LINE:
                    {
                        StopLine stopLine = (StopLine) elem;
                        data[eBase] = new GeneralElement(stopLine.getDistance());
                        if (stopLine.getHeading() != null) data[eBase + 1] = new GeneralElement(stopLine.getHeading());
                    }
                    break;
                case SPEED_BUMP:
                    {
                        SpeedBump speedBump = (SpeedBump) elem;
                        data[eBase] = new GeneralElement(speedBump.getDistance());
                        if (speedBump.getHeading() != null) data[eBase + 1] = new GeneralElement(speedBump.getHeading());
                    }
                    break;
                case ARROW_MARK:
                    {
                        ArrowMark arrowMark = (ArrowMark) elem;
                        data[eBase] = new GeneralElement(arrowMark.getArrows().isGoStraight() ? 1 : 0);
                        data[eBase + 1] = new GeneralElement(arrowMark.getArrows().isTurnLeft() ? 1 : 0);
                        data[eBase + 2] = new GeneralElement(arrowMark.getArrows().isTurnRight() ? 1 : 0);
                        data[eBase + 3] = new GeneralElement(arrowMark.getArrows().isUTurn() ? 1 : 0);
                        if (arrowMark.getPosX() != null) data[eBase + 4] = new GeneralElement(arrowMark.getPosX());
                        if (arrowMark.getPosY() != null) data[eBase + 5] = new GeneralElement(arrowMark.getPosY());
                    }
                    break;
                case PROHIBITION_ZONE:
                    {
                        ProhibitionZone prohibitionZone = (ProhibitionZone) elem;
                        data[eBase] = new GeneralElement(prohibitionZone.getCenterX());
                        data[eBase + 1] = new GeneralElement(prohibitionZone.getCenterY());
                        data[eBase + 2] = new GeneralElement(prohibitionZone.getWidth());
                        data[eBase + 3] = new GeneralElement(prohibitionZone.getLength());
                        if (prohibitionZone.getHeading() != null) data[eBase + 4] = new GeneralElement(prohibitionZone.getHeading());
                    }
                    break;
                case PARKING_SLOT:
                    {
                        ParkingSlot parkingSlot = (ParkingSlot) elem;
                        data[eBase] = new GeneralElement(parkingSlot.getCenterX());
                        data[eBase + 1] = new GeneralElement(parkingSlot.getCenterY());
                        data[eBase + 2] = new GeneralElement(parkingSlot.getWidth());
                        data[eBase + 3] = new GeneralElement(parkingSlot.getLength());
                        if (parkingSlot.getHeading() != null) data[eBase + 4] = new GeneralElement(parkingSlot.getHeading());
                        data[eBase + 5] = new GeneralElement(parkingSlot.getOccupancy().getValue());
                    }
                    break;
                case ZEBRA_CROSSING:
                    {
                        ZebraCrossing zebraCrossing = (ZebraCrossing) elem;
                        data[eBase] = new GeneralElement(zebraCrossing.getNearDistance());
                        if (zebraCrossing.getFarDistance() != null) data[eBase + 1] = new GeneralElement(zebraCrossing.getFarDistance());
                        if (zebraCrossing.getHeading() != null) data[eBase + 2] = new GeneralElement(zebraCrossing.getHeading());
                    }
                    break;
                default:
                    break;
            }
        }

        // 将数据添加到样本中
        sample.setValues(data);
        sample.setNumberOfSignificants(data.length);

        return sample;
    }

    /**
     * 从通用样本转换为交通样本
     */
    @Override
    public boolean fromGeneralSample(GeneralSample sample) {
        setTime(sample);
        
        String protocol = sample.getProtocol();
        
        // 根据协议版本调用相应的处理函数
        if ("traffic-sample-v2".equals(protocol)) {
            return fromTrafficSampleV2(sample);
        } else if ("traffic-sample-v1".equals(protocol)) {
            return fromTrafficSampleV1(sample);
        }
        
        return false;
    }
    
    /**
     * 从通用样本V2版本转换为交通样本
     */
    private boolean fromTrafficSampleV2(GeneralSample sample) {
        GeneralElement[] v = sample.getValues();
        if (v.length < 1) return false;
        if (isNotNumber(v[0])) return false;

        int count = toInt(v[0]);
        if (v.length < 1 + 6 * count) return false;

        if (count > 0) {
            int lastOffset = toInt(v[1 + 6 * (count - 1)]);
            int lastLength = toInt(v[2 + 6 * (count - 1)]);
            int totalDataSize = lastOffset + lastLength;
            if (v.length != 1 + 6 * count + totalDataSize) return false;
        }

        List<TrafficElement> list = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            if (isNotNumber(v[1 + 6 * i]) || isNotNumber(v[2 + 6 * i]) || 
                isNotNumber(v[3 + 6 * i]) || isNotNumber(v[4 + 6 * i])) continue;

            TrafficElementType type = TrafficElementType.fromValue(toInt(v[3 + 6 * i]));
            if (type == TrafficElementType.INVALID) continue;

            int bas = 1 + 6 * count + toInt(v[1 + 6 * i]);
            int len = toInt(v[2 + 6 * i]);
            int id = toInt(v[4 + 6 * i]);
            Integer rawId = isNumber(v[5 + 6 * i]) ? toInt(v[5 + 6 * i]) : null;

            TrafficElement newElem = null;
            switch (type) {
                case SPEED_LIMIT:
                    if (len != 6) break;
                    SpeedLimit speedLimit = new SpeedLimit();
                    speedLimit.setId(id);
                    speedLimit.setRawId(rawId);
                    speedLimit.setLimitedSpeed(toInt(v[bas]));
                    speedLimit.setPosX(toDouble(v[bas + 1]));
                    speedLimit.setPosY(toDouble(v[bas + 2]));
                    speedLimit.setPosZ(toDouble(v[bas + 3]));
                    newElem = speedLimit;
                    break;
                case TRAFFIC_SIGN:
                    if (len != 5) break;
                    TrafficSign trafficSign = new TrafficSign();
                    trafficSign.setId(id);
                    trafficSign.setRawId(rawId);
                    trafficSign.setSignType(isNumber(v[bas + 3]) ? 
                        TrafficSignType.fromValue(toInt(v[bas + 3])) : TrafficSignType.STOP);
                    trafficSign.setPosX(toDouble(v[bas]));
                    trafficSign.setPosY(toDouble(v[bas + 1]));
                    trafficSign.setPosZ(toDouble(v[bas + 2]));
                    newElem = trafficSign;
                    break;
                case TRAFFIC_LIGHT:
                    if (len != 10) break;
                    TrafficLight trafficLight = new TrafficLight();
                    trafficLight.setId(id);
                    trafficLight.setRawId(rawId);
                    trafficLight.setState(TrafficLightState.fromValue(toInt(v[bas])));
                    trafficLight.setPosX(toDouble(v[bas + 1]));
                    trafficLight.setPosY(toDouble(v[bas + 2]));
                    trafficLight.setPosZ(toDouble(v[bas + 3]));
                    trafficLight.setStopPosX(toDouble(v[bas + 8]));
                    trafficLight.setStopPosY(toDouble(v[bas + 9]));

                    if (isNumber(v[bas + 4]) && isNumber(v[bas + 5]) && 
                        isNumber(v[bas + 6]) && isNumber(v[bas + 7])) {
                        ArrowCombination arrows = new ArrowCombination();
                        arrows.setGoStraight(toInt(v[bas + 4]) == 1);
                        arrows.setTurnLeft(toInt(v[bas + 5]) == 1);
                        arrows.setTurnRight(toInt(v[bas + 6]) == 1);
                        arrows.setUTurn(toInt(v[bas + 7]) == 1);
                        trafficLight.setArrows(arrows);
                    }
                    newElem = trafficLight;
                    break;
                case STOP_LINE:
                    if (len != 4) break;
                    StopLine stopLine = new StopLine();
                    stopLine.setId(id);
                    stopLine.setRawId(rawId);
                    stopLine.setDistance(toDouble(v[bas]));
                    stopLine.setHeading(toDouble(v[bas + 1]));
                    newElem = stopLine;
                    break;
                case SPEED_BUMP:
                    if (len != 4) break;
                    SpeedBump speedBump = new SpeedBump();
                    speedBump.setId(id);
                    speedBump.setRawId(rawId);
                    speedBump.setDistance(toDouble(v[bas]));
                    speedBump.setHeading(toDouble(v[bas + 1]));
                    newElem = speedBump;
                    break;
                case ARROW_MARK:
                    if (len != 8) break;
                    ArrowMark arrowMark = new ArrowMark();
                    arrowMark.setId(id);
                    arrowMark.setRawId(rawId);
                    ArrowCombination arrowCombination = new ArrowCombination();
                    arrowCombination.setGoStraight(toInt(v[bas]) == 1);
                    arrowCombination.setTurnLeft(toInt(v[bas + 1]) == 1);
                    arrowCombination.setTurnRight(toInt(v[bas + 2]) == 1);
                    arrowCombination.setUTurn(toInt(v[bas + 3]) == 1);
                    arrowMark.setArrows(arrowCombination);
                    arrowMark.setPosX(toDouble(v[bas + 4]));
                    arrowMark.setPosY(toDouble(v[bas + 5]));
                    newElem = arrowMark;
                    break;
                case PROHIBITION_ZONE:
                    if (len != 7) break;
                    ProhibitionZone prohibitionZone = new ProhibitionZone();
                    prohibitionZone.setId(id);
                    prohibitionZone.setRawId(rawId);
                    prohibitionZone.setCenterX(toDouble(v[bas]));
                    prohibitionZone.setCenterY(toDouble(v[bas + 1]));
                    prohibitionZone.setWidth(toDouble(v[bas + 2]));
                    prohibitionZone.setLength(toDouble(v[bas + 3]));
                    prohibitionZone.setHeading(toDouble(v[bas + 4]));
                    newElem = prohibitionZone;
                    break;
                case PARKING_SLOT:
                    if (len != 7) break;
                    ParkingSlot parkingSlot = new ParkingSlot();
                    parkingSlot.setId(id);
                    parkingSlot.setRawId(rawId);
                    parkingSlot.setCenterX(toDouble(v[bas]));
                    parkingSlot.setCenterY(toDouble(v[bas + 1]));
                    parkingSlot.setWidth(toDouble(v[bas + 2]));
                    parkingSlot.setLength(toDouble(v[bas + 3]));
                    parkingSlot.setHeading(toDouble(v[bas + 4]));
                    parkingSlot.setOccupancy(isNumber(v[bas + 5]) ? 
                        ParkingSlotOccupancy.fromValue(toInt(v[bas + 5])) : ParkingSlotOccupancy.UNKNOWN);
                    newElem = parkingSlot;
                    break;
                case ZEBRA_CROSSING:
                    if (len != 5) break;
                    ZebraCrossing zebraCrossing = new ZebraCrossing();
                    zebraCrossing.setId(id);
                    zebraCrossing.setRawId(rawId);
                    zebraCrossing.setNearDistance(toDouble(v[bas]));
                    zebraCrossing.setFarDistance(toDouble(v[bas + 1]));
                    zebraCrossing.setHeading(toDouble(v[bas + 2]));
                    newElem = zebraCrossing;
                    break;
                default:
                    break;
            }
            if (newElem != null) list.add(newElem);
        }
        elements = list;
        return true;
    }
    
    /**
     * 从通用样本V1版本转换为交通样本
     */
    private boolean fromTrafficSampleV1(GeneralSample sample) {
        GeneralElement[] v = sample.getValues();
        if (v.length < 1) return false;
        if (isNotNumber(v[0])) return false;

        int count = toInt(v[0]);
        if (v.length < 1 + 3 * count) return false;

        if (count > 0) {
            int lastOffset = toInt(v[3 * count - 1]);
            int lastLength = toInt(v[3 * count]);
            int totalDataSize = lastOffset + lastLength;
            if (v.length < 1 + 3 * count + totalDataSize) return false;
        }

        List<TrafficElement> list = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            if (isNotNumber(v[1 + 3 * i]) || isNotNumber(v[2 + 3 * i])) continue;

            TrafficElementType type = TrafficElementType.fromValue(toInt(v[1 + 3 * i]));
            if (type == TrafficElementType.INVALID) continue;

            int bas = 1 + 3 * count + toInt(v[2 + 3 * i]);

            TrafficElement newElem = null;
            switch (type) {
                case SPEED_LIMIT:
                    SpeedLimit speedLimit = new SpeedLimit();
                    speedLimit.setLimitedSpeed(toInt(v[bas]));
                    speedLimit.setPosX(toDouble(v[bas + 1]));
                    speedLimit.setPosY(toDouble(v[bas + 2]));
                    newElem = speedLimit;
                    break;
                case TRAFFIC_SIGN:
                    TrafficSign trafficSign = new TrafficSign();
                    trafficSign.setSignType(TrafficSignType.STOP);
                    trafficSign.setPosX(toDouble(v[bas]));
                    trafficSign.setPosY(toDouble(v[bas + 1]));
                    newElem = trafficSign;
                    break;
                case TRAFFIC_LIGHT:
                    TrafficLight trafficLight = new TrafficLight();
                    trafficLight.setState(TrafficLightState.fromValue(toInt(v[bas])));
                    trafficLight.setPosX(toDouble(v[bas + 1]));
                    trafficLight.setPosY(toDouble(v[bas + 2]));

                    if (isNumber(v[bas + 3]) && isNumber(v[bas + 4]) && 
                        isNumber(v[bas + 5]) && isNumber(v[bas + 6])) {
                        ArrowCombination arrows = new ArrowCombination();
                        arrows.setGoStraight(toInt(v[bas + 3]) == 1);
                        arrows.setTurnLeft(toInt(v[bas + 4]) == 1);
                        arrows.setTurnRight(toInt(v[bas + 5]) == 1);
                        arrows.setUTurn(toInt(v[bas + 6]) == 1);
                        trafficLight.setArrows(arrows);
                    }
                    newElem = trafficLight;
                    break;
                case STOP_LINE:
                    StopLine stopLine = new StopLine();
                    stopLine.setDistance(toDouble(v[bas]));
                    stopLine.setHeading(toDouble(v[bas + 1]));
                    newElem = stopLine;
                    break;
                case SPEED_BUMP:
                    SpeedBump speedBump = new SpeedBump();
                    speedBump.setDistance(toDouble(v[bas]));
                    speedBump.setHeading(toDouble(v[bas + 1]));
                    newElem = speedBump;
                    break;
                case ARROW_MARK:
                    ArrowMark arrowMark = new ArrowMark();
                    ArrowCombination arrowCombination = new ArrowCombination();
                    arrowCombination.setGoStraight(toInt(v[bas]) == 1);
                    arrowCombination.setTurnLeft(toInt(v[bas + 1]) == 1);
                    arrowCombination.setTurnRight(toInt(v[bas + 2]) == 1);
                    arrowCombination.setUTurn(toInt(v[bas + 3]) == 1);
                    arrowMark.setArrows(arrowCombination);
                    arrowMark.setPosX(toDouble(v[bas + 4]));
                    arrowMark.setPosY(toDouble(v[bas + 5]));
                    newElem = arrowMark;
                    break;
                case PROHIBITION_ZONE:
                    ProhibitionZone prohibitionZone = new ProhibitionZone();
                    prohibitionZone.setCenterX(toDouble(v[bas]));
                    prohibitionZone.setCenterY(toDouble(v[bas + 1]));
                    prohibitionZone.setWidth(toDouble(v[bas + 2]));
                    prohibitionZone.setLength(toDouble(v[bas + 3]));
                    prohibitionZone.setHeading(toDouble(v[bas + 4]));
                    newElem = prohibitionZone;
                    break;
                case PARKING_SLOT:
                    ParkingSlot parkingSlot = new ParkingSlot();
                    parkingSlot.setCenterX(toDouble(v[bas]));
                    parkingSlot.setCenterY(toDouble(v[bas + 1]));
                    parkingSlot.setWidth(toDouble(v[bas + 2]));
                    parkingSlot.setLength(toDouble(v[bas + 3]));
                    parkingSlot.setHeading(toDouble(v[bas + 4]));
                    newElem = parkingSlot;
                    break;
                case ZEBRA_CROSSING:
                    ZebraCrossing zebraCrossing = new ZebraCrossing();
                    zebraCrossing.setNearDistance(toDouble(v[bas]));
                    zebraCrossing.setFarDistance(toDouble(v[bas + 1]));
                    zebraCrossing.setHeading(toDouble(v[bas + 2]));
                    newElem = zebraCrossing;
                    break;
                default:
                    break;
            }
            if (newElem != null) list.add(newElem);
        }
        elements = list;
        return true;
    }
    
    /**
     * 判断元素是否不是数字
     */
    private boolean isNotNumber(GeneralElement element) {
        return element == null || !isNumber(element);
    }
    
    /**
     * 判断GeneralElement是否是数字
     */
    private boolean isNumber(GeneralElement element) {
        return element != null && element.isValid() && !element.isText();
    }
    
    /**
     * 将GeneralElement转换为整数
     */
    private int toInt(GeneralElement element) {
        if (element == null || !isNumber(element)) {
            return 0;
        }
        return (int) element.getValue();
    }
    
    /**
     * 将GeneralElement转换为双精度浮点数
     */
    private double toDouble(GeneralElement element) {
        if (element == null || !isNumber(element)) {
            return 0.0;
        }
        return element.getValue();
    }
    
    /**
     * 是否支持插值
     */
    @Override
    public boolean supportInterpolation() {
        return true;
    }
    
    /**
     * 对两个交通样本进行插值
     */
    @Override
    public Sample interpolate(SearchResult result) {
        TrafficSample s1 = (TrafficSample)result.getS1();
        TrafficSample s2 = (TrafficSample)result.getS2();
        double w1 = result.getW1();
        double w2 = result.getW2();

        // 检查是否有无效的ID，如果有则采用权重最大的样本
        boolean noID = false;
        for (TrafficElement elem : s1.elements) {
            if (elem.getId() <= 0) {
                noID = true;
                break;
            }
        }
        for (TrafficElement elem : s2.elements) {
            if (elem.getId() <= 0) {
                noID = true;
                break;
            }
        }

        if (noID) {
            return w1 > w2 ? s1 : s2;
        }

        TrafficSample output = new TrafficSample();

        // 根据ID匹配元素并进行插值
        List<TrafficElement> elems = new ArrayList<>();
        for (int i = 0; i < s1.elements.size(); i++) {
            TrafficElement s1ElemB = s1.elements.get(i);
            for (int j = 0; j < s2.elements.size(); j++) {
                TrafficElement s2ElemB = s2.elements.get(j);

                if (s1ElemB.getId() == s2ElemB.getId() && s1ElemB.getType() == s2ElemB.getType()) {
                    TrafficElement elem = null;
                    switch (s1ElemB.getType()) {
                        case SPEED_LIMIT:
                            {
                                SpeedLimit s1Elem = (SpeedLimit)s1ElemB;
                                SpeedLimit s2Elem = (SpeedLimit)s2ElemB;
                                SpeedLimit newElem = new SpeedLimit();
                                newElem.setId(s1Elem.getId());
                                newElem.setRawId(w1 > w2 ? s1Elem.getRawId() : s2Elem.getRawId());
                                newElem.setLimitedSpeed(w1 > w2 ? s1Elem.getLimitedSpeed() : s2Elem.getLimitedSpeed());
                                
                                if (s1Elem.getPosX() != null && s2Elem.getPosX() != null) {
                                    newElem.setPosX(s1Elem.getPosX() * w1 + s2Elem.getPosX() * w2);
                                }
                                if (s1Elem.getPosY() != null && s2Elem.getPosY() != null) {
                                    newElem.setPosY(s1Elem.getPosY() * w1 + s2Elem.getPosY() * w2);
                                }
                                if (s1Elem.getPosZ() != null && s2Elem.getPosZ() != null) {
                                    newElem.setPosZ(s1Elem.getPosZ() * w1 + s2Elem.getPosZ() * w2);
                                }
                                elem = newElem;
                            }
                            break;
                        case TRAFFIC_SIGN:
                            {
                                TrafficSign s1Elem = (TrafficSign)s1ElemB;
                                TrafficSign s2Elem = (TrafficSign)s2ElemB;
                                TrafficSign newElem = new TrafficSign();
                                newElem.setId(s1Elem.getId());
                                newElem.setRawId(w1 > w2 ? s1Elem.getRawId() : s2Elem.getRawId());
                                newElem.setSignType(w1 > w2 ? s1Elem.getSignType() : s2Elem.getSignType());
                                
                                if (s1Elem.getPosX() != null && s2Elem.getPosX() != null) {
                                    newElem.setPosX(s1Elem.getPosX() * w1 + s2Elem.getPosX() * w2);
                                }
                                if (s1Elem.getPosY() != null && s2Elem.getPosY() != null) {
                                    newElem.setPosY(s1Elem.getPosY() * w1 + s2Elem.getPosY() * w2);
                                }
                                if (s1Elem.getPosZ() != null && s2Elem.getPosZ() != null) {
                                    newElem.setPosZ(s1Elem.getPosZ() * w1 + s2Elem.getPosZ() * w2);
                                }
                                elem = newElem;
                            }
                            break;
                        case TRAFFIC_LIGHT:
                            {
                                TrafficLight s1Elem = (TrafficLight)s1ElemB;
                                TrafficLight s2Elem = (TrafficLight)s2ElemB;
                                TrafficLight newElem = new TrafficLight();
                                newElem.setId(s1Elem.getId());
                                newElem.setRawId(w1 > w2 ? s1Elem.getRawId() : s2Elem.getRawId());
                                newElem.setState(w1 > w2 ? s1Elem.getState() : s2Elem.getState());
                                newElem.setArrows(w1 > w2 ? s1Elem.getArrows() : s2Elem.getArrows());
                                
                                if (s1Elem.getPosX() != null && s2Elem.getPosX() != null) {
                                    newElem.setPosX(s1Elem.getPosX() * w1 + s2Elem.getPosX() * w2);
                                }
                                if (s1Elem.getPosY() != null && s2Elem.getPosY() != null) {
                                    newElem.setPosY(s1Elem.getPosY() * w1 + s2Elem.getPosY() * w2);
                                }
                                if (s1Elem.getPosZ() != null && s2Elem.getPosZ() != null) {
                                    newElem.setPosZ(s1Elem.getPosZ() * w1 + s2Elem.getPosZ() * w2);
                                }
                                if (s1Elem.getStopPosX() != null && s2Elem.getStopPosX() != null) {
                                    newElem.setStopPosX(s1Elem.getStopPosX() * w1 + s2Elem.getStopPosX() * w2);
                                }
                                if (s1Elem.getStopPosY() != null && s2Elem.getStopPosY() != null) {
                                    newElem.setStopPosY(s1Elem.getStopPosY() * w1 + s2Elem.getStopPosY() * w2);
                                }
                                elem = newElem;
                            }
                            break;
                        case STOP_LINE:
                            {
                                StopLine s1Elem = (StopLine)s1ElemB;
                                StopLine s2Elem = (StopLine)s2ElemB;
                                StopLine newElem = new StopLine();
                                newElem.setId(s1Elem.getId());
                                newElem.setRawId(w1 > w2 ? s1Elem.getRawId() : s2Elem.getRawId());
                                newElem.setDistance(s1Elem.getDistance() * w1 + s2Elem.getDistance() * w2);
                                
                                if (s1Elem.getHeading() != null && s2Elem.getHeading() != null) {
                                    newElem.setHeading(s1Elem.getHeading() * w1 + s2Elem.getHeading() * w2);
                                }
                                elem = newElem;
                            }
                            break;
                        case SPEED_BUMP:
                            {
                                SpeedBump s1Elem = (SpeedBump)s1ElemB;
                                SpeedBump s2Elem = (SpeedBump)s2ElemB;
                                SpeedBump newElem = new SpeedBump();
                                newElem.setId(s1Elem.getId());
                                newElem.setRawId(w1 > w2 ? s1Elem.getRawId() : s2Elem.getRawId());
                                newElem.setDistance(s1Elem.getDistance() * w1 + s2Elem.getDistance() * w2);
                                
                                if (s1Elem.getHeading() != null && s2Elem.getHeading() != null) {
                                    newElem.setHeading(s1Elem.getHeading() * w1 + s2Elem.getHeading() * w2);
                                }
                                elem = newElem;
                            }
                            break;
                        case ARROW_MARK:
                            {
                                ArrowMark s1Elem = (ArrowMark)s1ElemB;
                                ArrowMark s2Elem = (ArrowMark)s2ElemB;
                                ArrowMark newElem = new ArrowMark();
                                newElem.setId(s1Elem.getId());
                                newElem.setRawId(w1 > w2 ? s1Elem.getRawId() : s2Elem.getRawId());
                                newElem.setArrows(w1 > w2 ? s1Elem.getArrows() : s2Elem.getArrows());
                                
                                if (s1Elem.getPosX() != null && s2Elem.getPosX() != null) {
                                    newElem.setPosX(s1Elem.getPosX() * w1 + s2Elem.getPosX() * w2);
                                }
                                if (s1Elem.getPosY() != null && s2Elem.getPosY() != null) {
                                    newElem.setPosY(s1Elem.getPosY() * w1 + s2Elem.getPosY() * w2);
                                }
                                elem = newElem;
                            }
                            break;
                        case PROHIBITION_ZONE:
                            {
                                ProhibitionZone s1Elem = (ProhibitionZone)s1ElemB;
                                ProhibitionZone s2Elem = (ProhibitionZone)s2ElemB;
                                ProhibitionZone newElem = new ProhibitionZone();
                                newElem.setId(s1Elem.getId());
                                newElem.setRawId(w1 > w2 ? s1Elem.getRawId() : s2Elem.getRawId());
                                newElem.setCenterX(s1Elem.getCenterX() * w1 + s2Elem.getCenterX() * w2);
                                newElem.setCenterY(s1Elem.getCenterY() * w1 + s2Elem.getCenterY() * w2);
                                newElem.setWidth(s1Elem.getWidth() * w1 + s2Elem.getWidth() * w2);
                                newElem.setLength(s1Elem.getLength() * w1 + s2Elem.getLength() * w2);
                                
                                if (s1Elem.getHeading() != null && s2Elem.getHeading() != null) {
                                    newElem.setHeading(s1Elem.getHeading() * w1 + s2Elem.getHeading() * w2);
                                }
                                elem = newElem;
                            }
                            break;
                        case PARKING_SLOT:
                            {
                                ParkingSlot s1Elem = (ParkingSlot)s1ElemB;
                                ParkingSlot s2Elem = (ParkingSlot)s2ElemB;
                                ParkingSlot newElem = new ParkingSlot();
                                newElem.setId(s1Elem.getId());
                                newElem.setRawId(w1 > w2 ? s1Elem.getRawId() : s2Elem.getRawId());
                                newElem.setCenterX(s1Elem.getCenterX() * w1 + s2Elem.getCenterX() * w2);
                                newElem.setCenterY(s1Elem.getCenterY() * w1 + s2Elem.getCenterY() * w2);
                                newElem.setWidth(s1Elem.getWidth() * w1 + s2Elem.getWidth() * w2);
                                newElem.setLength(s1Elem.getLength() * w1 + s2Elem.getLength() * w2);
                                
                                if (s1Elem.getHeading() != null && s2Elem.getHeading() != null) {
                                    newElem.setHeading(s1Elem.getHeading() * w1 + s2Elem.getHeading() * w2);
                                }
                                newElem.setOccupancy(w1 > w2 ? s1Elem.getOccupancy() : s2Elem.getOccupancy());
                                elem = newElem;
                            }
                            break;
                        case ZEBRA_CROSSING:
                            {
                                ZebraCrossing s1Elem = (ZebraCrossing)s1ElemB;
                                ZebraCrossing s2Elem = (ZebraCrossing)s2ElemB;
                                ZebraCrossing newElem = new ZebraCrossing();
                                newElem.setId(s1Elem.getId());
                                newElem.setRawId(w1 > w2 ? s1Elem.getRawId() : s2Elem.getRawId());
                                newElem.setNearDistance(s1Elem.getNearDistance() * w1 + s2Elem.getNearDistance() * w2);
                                
                                if (s1Elem.getFarDistance() != null && s2Elem.getFarDistance() != null) {
                                    newElem.setFarDistance(s1Elem.getFarDistance() * w1 + s2Elem.getFarDistance() * w2);
                                }
                                if (s1Elem.getHeading() != null && s2Elem.getHeading() != null) {
                                    newElem.setHeading(s1Elem.getHeading() * w1 + s2Elem.getHeading() * w2);
                                }
                                elem = newElem;
                            }
                            break;
                        default:
                            break;
                    }

                    if (elem != null) elems.add(elem);
                    break;
                }
            }
        }
        output.elements = elems;

        return output;
    }
    
    public static final String TARGET_PROTOCOL = "traffic-sample-v2";
    public static final String[] PROTOCOLS = { "traffic-sample-v1", "traffic-sample-v2" };
}